#include "mpStringUtils.h"
#include "mpCodecUtils.h"
#include "mpUrl.h"
#include "mpHttpException.h"

#include <boost/lexical_cast.hpp>

namespace MP{
namespace HTTP{

bool checkIsUseHttps(const std::string & schema){
    return schema == "https";
}

void Url::setHostAndPort(const std::string & strDomain){

    std::vector<std::string> hostAndPortVec;
    MP::StringUtils::splitString(strDomain,":",hostAndPortVec);

    if(hostAndPortVec.size() !=1 && hostAndPortVec.size() != 2){
        throw HttpException("Invalid Host and Port . strDomain :" + strDomain);
    }

    setHost(hostAndPortVec[0]);

    if(hostAndPortVec.size() == 1){
        if(_schema == "http"){
            setPort(80);
        }
        else{
            setPort(443);
        }
    }
    else{

        setPort(boost::lexical_cast<unsigned short>(hostAndPortVec[1]));
    }
}

void Url::parseUrl(const std::string & url){
    //schema://host:port/path?queries#fragment

    size_t start = 0, index = 0;

    index = url.find("://",start);
    if(index == std::string::npos){
        throw HttpException("Invalid URL,no schema supplied:" + url);
    }
    //解析schema
    setScheme(url.substr(start, index));
    start = index + 3;


    //schema://host:port
    //schema://host:port/path
    //schema://host:port/path#fragment
    //schema://host:port/path?queries
    //schema://host:port/path?queries#fragment
    //schema://host:port?queries
    //schema://host:port#fragment

    size_t substrIndex = 0;

    size_t domainIndex = url.find("/",start);
    if(domainIndex != std::string::npos){
        substrIndex = domainIndex;
    }

    size_t queriesIndex = url.find("?",start);
    if(queriesIndex != std::string::npos && (substrIndex == 0 || queriesIndex < substrIndex)){
        substrIndex = queriesIndex;
    }

    size_t fragmentIndex = url.find("#",start);
    if(fragmentIndex != std::string::npos && (substrIndex == 0 || fragmentIndex < substrIndex)){
        substrIndex = fragmentIndex;
    }

    if(substrIndex == 0){
        //url中字符 / ? # 均不存在
        std::string strDomainName = url.substr(start);
        setHostAndPort(strDomainName);
        setRelativeUri("");
    }
    else{
        //url中字符 / ? # 至少存在一个
        std::string strDomainName = url.substr(start,substrIndex-start);
        setHostAndPort(strDomainName);

        if(substrIndex < url.size()){
            setRelativeUri(url.substr(substrIndex));
        }
    }
}


Url::Url(const std::string & url)
    :_schema(""),
      _host(""),
      _port(80),
      _relativeUri("")
{
    setBaseUrl(url);
}

Url::Url(const Url & baseUrl,
         const std::string & relativeUri)
    : Url(baseUrl.getScheme(),baseUrl.getHost(),baseUrl.getPort(),relativeUri){


}

Url::Url(const std::string & schema,
         const std::string & host,
         unsigned short port,
         const std::string & relativeUri)
{
    setData(schema,host,port,relativeUri);
}

Url::Url(const Url & c){
    setScheme(c.getScheme());
    setHost(c.getHost());
    setPort(c.getPort());
    setRelativeUri(c.getRelativeUri());
}


void Url::setBaseUrl(const std::string & url){
    std::string lowerUrl = MP::StringUtils::stringToLower_copy(url);
    MP::StringUtils::trim_copy(lowerUrl);

    parseUrl(url);
}

void Url::setData(const std::string & schema,
             const std::string & host,
             unsigned short port,
             const std::string & relativeUri){
    setScheme(schema);
    setHost(host);
    setPort(port);
    setRelativeUri(relativeUri);
}


void Url::setScheme(const std::string& scheme){
    if(scheme != "http"){
        _schema = "https";
    }
    else{
        _schema = "http";
    }
}

void Url::setHost(const std::string& host){

    std::string tmp = MP::StringUtils::trim_copy(host);
    if(tmp.empty()){
        throw HttpException("can not set empty for host");
    }
    _host = tmp;

}

void Url::setPort(unsigned short port){
    _port = port;

}

void Url::setRelativeUri(const std::string& relativeUri){

    std::string tmp = MP::StringUtils::trim_copy(relativeUri);

    //utf8Url_encode
    MP::CodecUtils::utf8Url_encode(_relativeUri,tmp);

    //    std::string tmp = MP::StringUtils::trim_copy(scheme);
    //    if(tmp.empty()){
    //        _relativeUri = "";
    //    }
    //    else{
    //        if (MP::StringUtils::stringStartsWith(tmp,"/")) {
    //            _relativeUri = tmp.substr(1);
    //        } else {
    //            _relativeUri = tmp;
    //        }
    //    }
}

std::string Url::toString() const{
    std::string strUrl;

    strUrl.append(_schema);
    strUrl.append("://");
    strUrl.append(_host);

    if((_schema == "http" && _port != 80)
            || (_schema == "https" && _port != 443)){
        strUrl.append(":");
        strUrl.append(boost::lexical_cast<std::string>(_port));
    }

    if(!_relativeUri.empty()){
        if(!MP::StringUtils::stringStartsWith(_relativeUri,"/")
                && !MP::StringUtils::stringStartsWith(_relativeUri,"?")
                && !MP::StringUtils::stringStartsWith(_relativeUri,"#")){
            strUrl.append("/");
        }
        strUrl.append(_relativeUri);
    }

    return strUrl;
}


//void Url::setHostAndPort(const std::string & strDomain){

//    std::vector<std::string> hostAndPortVec;
//    MP::StringUtils::splitString(strDomain,":",hostAndPortVec);

//    if(hostAndPortVec.size() !=1 && hostAndPortVec.size() != 2){
//        throw HttpException("Invalid Host and Port . strDomain :" + strDomain);
//    }

//    setHost(hostAndPortVec[0]);

//    if(hostAndPortVec.size() == 1){
//        if(_schema == "http"){
//            setPort(80);
//        }
//        else{
//            setPort(443);
//        }
//    }
//    else{

//        setPort(boost::lexical_cast<unsigned short>(hostAndPortVec[1]));
//    }
//}

//void Url::parseUrl(const std::string & url){
//    //schema://host:port/path?queries#fragment

//    size_t start = 0, index = 0;

//    index = url.find("://",start);
//    if(index == std::string::npos){
//        throw HttpException("Invalid URL,no schema supplied:" + url);
//    }
//    //解析schema
//    setScheme(url.substr(start, index));
//    start = index + 3;

//    size_t domainIndex = url.find("/",start);
//    size_t queriesIndex = url.find("?",start);
//    size_t fragmentIndex = url.find("#",start);

//    //schema://host:port
//    //schema://host:port/path
//    //schema://host:port/path#fragment
//    //schema://host:port/path?queries
//    //schema://host:port/path?queries#fragment
//    //schema://host:port?queries
//    //schema://host:port#fragment


//    //schema://host:port
//    if(domainIndex == std::string::npos
//            && queriesIndex == std::string::npos
//            && fragmentIndex == std::string::npos){

//        std::string strDomainName = url.substr(start);
//        setHostAndPort(strDomainName);
//    }

//    //schema://host:port/path
//    if(domainIndex != std::string::npos
//            && queriesIndex == std::string::npos
//            && fragmentIndex == std::string::npos){

//        std::string strDomainName = url.substr(start,domainIndex-start);
//        setHostAndPort(strDomainName);

//        if(domainIndex+1 < url.size()){
//            setPath(url.substr(domainIndex+1));
//        }
//    }


//    //schema://host:port/path#fragment
//    if(domainIndex != std::string::npos
//            && queriesIndex == std::string::npos
//            && fragmentIndex != std::string::npos){

//        std::string strDomainName = url.substr(start,domainIndex-start);
//        setHostAndPort(strDomainName);

//        if(domainIndex + 1 < fragmentIndex){
//            setPath(url.substr(domainIndex+1,fragmentIndex-domainIndex-1));
//        }

//        if(fragmentIndex + 1 < url.size()){
//            setFragment(url.substr(fragmentIndex+1));
//        }
//    }

//    //schema://host:port/path?queries
//    if(domainIndex != std::string::npos
//            && queriesIndex != std::string::npos
//            && fragmentIndex == std::string::npos){

//        std::string strDomainName = url.substr(start,domainIndex-start);
//        setHostAndPort(strDomainName);

//        if(domainIndex + 1 < queriesIndex){
//            setPath(url.substr(domainIndex+1,queriesIndex-domainIndex-1));
//        }

//        if(queriesIndex + 1 < url.size()){
//            std::string strQueries = url.substr(queriesIndex+1);
//            setQueries(strQueries);
//        }

//    }

//    //schema://host:port/path?queries#fragment
//    if(domainIndex != std::string::npos
//            && queriesIndex != std::string::npos
//            && fragmentIndex != std::string::npos){

//        std::string strDomainName = url.substr(start,domainIndex-start);
//        setHostAndPort(strDomainName);

//        if(domainIndex + 1 < queriesIndex){
//            setPath(url.substr(domainIndex+1,queriesIndex-domainIndex-1));
//        }

//        if(queriesIndex + 1 < fragmentIndex){
//            std::string strQueries = url.substr(queriesIndex+1,fragmentIndex-queriesIndex-1);
//            setQueries(strQueries);
//        }

//        if(fragmentIndex + 1 < url.size()){
//            setFragment(url.substr(fragmentIndex+1));
//        }

//    }

//    //schema://host:port?queries
//    if(domainIndex == std::string::npos
//            && queriesIndex != std::string::npos
//            && fragmentIndex == std::string::npos){

//        std::string strDomainName = url.substr(start,queriesIndex-start);
//        setHostAndPort(strDomainName);

//        if(queriesIndex + 1 < url.size()){
//            std::string strQueries = url.substr(queriesIndex+1);
//            setQueries(strQueries);
//        }
//    }

//    //schema://host:port#fragment
//    if(domainIndex == std::string::npos
//            && queriesIndex == std::string::npos
//            && fragmentIndex != std::string::npos){

//        std::string strDomainName = url.substr(start,fragmentIndex-start);
//        setHostAndPort(strDomainName);

//        if(fragmentIndex + 1 < url.size()){
//            setFragment(url.substr(fragmentIndex+1));
//        }

//    }
//}


//void Url::addQueries(const std::string & key,
//                     const std::string & val){
//    if(!key.empty()){
//        _queries[key] = val;
//    }
//}

//void Url::addQueries(const std::unordered_map<std::string, std::string> & params){

//    for(auto &it : params) // 引用也可以不加，当需要节省空间时可以加上
//    {
//        addQueries(it.first,it.second);
//    }
//}

//void Url::setQueries(const std::string & queries){

//    if(queries.empty()){
//        return;
//    }
//    //key1=val1&key2=val2

//    std::vector<std::string> qVec;
//    MP::StringUtils::splitString(queries,"&",qVec);

//    for(size_t i =0 ;i < qVec.size();i++){
//        std::string item = qVec[i];
//        size_t index = item.find("=",0);
//        if(index == std::string::npos){
//            throw HttpException("Invalid queries,no equal sign . queries :" + item);
//        }

//        if(index+1 == item.size()){
//            addQueries(item.substr(0,index),"");
//        }else{
//            addQueries(item.substr(0,index),item.substr(index+1));
//        }
//    }
//}

//void Url::clearQuery(){
//    _queries.clear();
//}

}   //HTTP
}   //MP
